function TextStyler(textModel){
    this.initTokenizer();
    this.init$Eventable();
    this.$textModel = textModel;
    this.updateTokens(0, 0, textModel.getText());
    this.on(this.$textModel, 'Changing', this._onTextChanging);
    this.$scanner = new JavascriptScanner();
}

function Token(type, length){
    this.type = type;
    this.length = length;
}
Token.prototype.slice = function (startOffset, endOffset){
    var r = Utils.calcSliceArguments(startOffset, endOffset, this.length);
    return new Token(this.type, r[1] - r[0]);
};

TextStyler.prototype = {
    getTokens: function (startOffset, endOffset){
        var tokens = [];
        this.walkTokens(startOffset, endOffset, function (token){
            tokens.push(token);
        });
        return tokens;
    },
    _onTextChanging: function (evt){
        var offset = evt.offset;
        this.$dirtyOffset = offset;
        this.updateTokens(offset, offset + evt.deletedText.length, evt.insertedText);
        this.update();
    },
    update: function (){
        var dirtyOffset = this.$dirtyOffset;
        var r1 = this.findNode(dirtyOffset);
        var node = r1.node;
        var nodeOffset = r1.nodeOffset;
        var startOffset = r1.nodeOffset;
        var scanner = this.$scanner;
        var text = this.$textModel.getText(startOffset);
        scanner.setText(text);
        scanner.setCondition(1);

        var tokens = [];
        var endOffset = text.length;
        var tokenOffset = 0;
        var lastOffset;
        var succ = false;
        while ((lastOffset = scanner.nextToken(tokenOffset)) <= endOffset) {
            tokens.push(new Token(scanner.getTokenType(), lastOffset - tokenOffset));
            while (node != null) {
                if (nodeOffset >= startOffset + lastOffset) {
                    break;
                }
                nodeOffset += node.length;
                node = node.next;
            }
            if (nodeOffset === startOffset + lastOffset) {
                succ = true;
                break;
            }
            tokenOffset = lastOffset;
        }
        if (!succ && tokenOffset < endOffset) {
            tokens.push(new Token(null, endOffset - tokenOffset));
            lastOffset = endOffset;
        }
        if (tokens.length) {
            this.updateNodes(startOffset, startOffset + lastOffset, this.createNodes1(tokens));
        }
    },
    parseTokens: function (text, callback){
        callback(new Token(null, text.length));
    },
    concatTwoTokens: function (tokenA, tokenB){
        if (tokenA.type == tokenB.type) {
            return new Token(tokenA.type, tokenA.length + tokenB.length);
        }
        return null;
    }
};

Utils.extend(TextStyler, Eventable);
Utils.extend(TextStyler, Tokenizer);
